package com.xxl.job.core.thread;

import com.xxl.job.core.biz.model.HandleCallbackParam;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import com.xxl.job.core.context.XxlJobContext;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.log.XxlJobFileAppender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;


/**
 * handler thread
 *
 * @author xuxueli 2016-1-16 19:52:47
 * 具体执行任务的执行器线程
 */
public class JobTask implements Runnable {
    private static Logger logger = LoggerFactory.getLogger(JobTask.class);

    private TriggerParam triggerParam;
    private IJobHandler handler;


    public JobTask(IJobHandler handler, TriggerParam triggerParam) {
        this.handler = handler;
        this.triggerParam = triggerParam;
    }

    @Override
    public void run() {

        // init
        try {
            handler.init();
        } catch (Throwable e) {
            logger.error("handler init error,job task stop.", e);
            return;
        }


        // log filename, like "logPath/yyyy-MM-dd/9999.log"
        String logFileName = XxlJobFileAppender.makeLogFileName(new Date(triggerParam.getLogDateTime()), triggerParam.getLogId());
        XxlJobContext xxlJobContext = XxlJobContext.createXxlJobContext(triggerParam, logFileName);
        // init job context
        XxlJobContext.setXxlJobContext(xxlJobContext);

        long startTime = System.currentTimeMillis();
        //handle
        try {
            XxlJobHelper.log("<br>----------- xxl-job job execute start -----------<br>----------- Param:" + xxlJobContext.getJobParam());
            if (triggerParam.getExecutorTimeout() > 0) {
                // limit timeout
                Thread futureThread = null;
                try {
                    long finalStartTime = startTime;
                    FutureTask<Boolean> futureTask = new FutureTask<>(() -> {

                        // init job context
                        XxlJobContext.setXxlJobContext(xxlJobContext);
                        XxlJobContext.getXxlJobContext().setStartExeTime(finalStartTime);
                        handler.execute();
                        XxlJobContext.getXxlJobContext().setCost(System.currentTimeMillis() - finalStartTime);
                        return true;
                    });
                    futureThread = new Thread(futureTask);
                    futureThread.start();

                    futureTask.get(triggerParam.getExecutorTimeout(), TimeUnit.SECONDS);
                } catch (TimeoutException e) {
                    XxlJobHelper.log("<br>----------- xxl-job job execute timeout");
                    XxlJobHelper.log(e);
                    // handle result
                    XxlJobHelper.handleTimeout("job execute timeout ");
                } finally {
                    futureThread.interrupt();
                }
            } else {
                XxlJobContext.getXxlJobContext().setStartExeTime(startTime);
                // just execute
                handler.execute();
                XxlJobContext.getXxlJobContext().setCost(System.currentTimeMillis() - startTime);
            }

            XxlJobHelper.log("<br>----------- xxl-job job execute end(finish) -----------<br>----------- Result: handleCode="
                    + XxlJobContext.getXxlJobContext().getHandleCode()
                    + ", handleMsg = "
                    + XxlJobContext.getXxlJobContext().getHandleMsg());

        } catch (Throwable e) {
            XxlJobContext.getXxlJobContext().setStartExeTime(startTime);
            // handle result
            StringWriter stringWriter = new StringWriter();
            e.printStackTrace(new PrintWriter(stringWriter));
            String errorMsg = stringWriter.toString();
            errorMsg = errorMsg.length() > 50000 ? errorMsg.substring(0, 50000).concat("...") : errorMsg;

            XxlJobHelper.handleFail(errorMsg, System.currentTimeMillis() - startTime);
            XxlJobHelper.log("<br>----------- JobThread Exception:" + errorMsg + "<br>----------- xxl-job job execute end(error) -----------");
        } finally {
            if(triggerParam.getLogId() <= 0L){
                TriggerCallbackThread.pushCallBack(new HandleCallbackParam(
                        triggerParam.getLogUUID(),
                        triggerParam.getLogDateTime(),
                        XxlJobContext.getXxlJobContext().getHandleCode(),
                        XxlJobContext.getXxlJobContext().getHandleMsg(),
                        XxlJobContext.getXxlJobContext().getCost(),
                        XxlJobContext.getXxlJobContext().getStartExeTime())
                );
            }else {
                TriggerCallbackThread.pushCallBack(new HandleCallbackParam(
                        triggerParam.getLogId(),
                        triggerParam.getLogDateTime(),
                        XxlJobContext.getXxlJobContext().getHandleCode(),
                        XxlJobContext.getXxlJobContext().getHandleMsg(),
                        XxlJobContext.getXxlJobContext().getCost(),
                        XxlJobContext.getXxlJobContext().getStartExeTime())
                );
            }

        }


        // destroy
        try {
            handler.destroy();
        }catch(Throwable e) {
            logger.error(e.getMessage(), e);
        }

    }


}

